package commons;

import java.io.*;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

import commons.exceptions.PasswordException;

/**
 * The User class is used to create new user and manage them in a object of type UserList
 * @author 
 */
public class User implements Comparable<User>,Serializable{		

	private static final long serialVersionUID	= 1L; // avoid a warning
	
	// Errors
	private static final String PASSWORD_MUST_BE_4_DIGITS		= "The password must be 4 digits, try again !";
	private static final String CURRENT_PASSWORD_IS_EMPTY		= "You need to give the current password in order to change it.";
	private static final String CURRENT_PASSWORD_IS_WRONG		= "The given current password is wrong.";
	private static final String ERROR_FILE						= "Weird error with the passwords' file.";
	private static final String MD5_ERROR						= "Something went wrong with MD5";
	private static final String THIS_IS_NOT_A_VALID_PASSWORD	= "This is not the password of a valid user!"; // pour isHeAnAdminOrAguest(String pwd), displayed on Connection
	
	private String name;
	private boolean isAdmin;
	private long timeStamp;
	private int idSession;

	/**
	 * Constructor of the class
	 * @param name : the name of the user (10 char. max)
	 * @param isAdmin : if the user is an admin true, false otherwise
	 * @throws IllegalArgumentException When the name is longer than 10 char.
	 */
	public User(String name, boolean isAdmin) throws IllegalArgumentException {
		if (name == null || name.trim().isEmpty())
			name = (isAdmin) ? "Admin" : "Guest";
		else if (name.length()>10)
			throw new IllegalArgumentException("Nickname is too long (10 char. max)");
		this.name=name;
		this.isAdmin=isAdmin;
		this.timeStamp=System.currentTimeMillis();
		this.idSession=this.hashCode();
	}

	/**
	 * Get the user's name
	 * @return the name of the user
	 */
	public String getName() {
		return name;
	}

	/**
	 * Set of the user's name
	 * @param name : the new name of the user
	 */
	public void setName(String name) {
		this.name = name;
	}

	/**
	 * Get the user's rights
	 * @return true if the user is an admin, false otherwise
	 */
	public boolean isAdmin() {
		return isAdmin;
	}

	/**
	 * Set the user's rights
	 * @param isAdmin : true if the user is an admin, false otherwise
	 */
	public void setAdmin(boolean isAdmin) {
		this.isAdmin = isAdmin;
	}


	private static String encodeMD5(String passwd) throws NoSuchAlgorithmException, UnsupportedEncodingException {
		MessageDigest m = MessageDigest.getInstance("MD5");
		m.update(passwd.getBytes("UTF8"));
		byte s[] = m.digest();
		String result = "";
		for (int i = 0; i < s.length; i++) {
			result += Integer.toHexString((0x000000ff & s[i]) | 0xffffff00).substring(6);
		}
		return result;
	}

	/**
	 * Check that the password is valid (4 digits)
	 * @param pwd The tested password
	 * @return True if the password is valid, false otherwise
	 */
	private static boolean isThePasswordValid(String pwd) {
		return (pwd!=null && pwd.matches("^\\d{4}$"));
	}
	
	/**
	 * Set the admin password
	 * @param currentPassword The current password
	 * @param newPassword The new password
	 * @throws PasswordException When something went wrong
	 */
	public static void setAdminPassword(String currentPassword, String newPassword) throws PasswordException {
		if (!isThePasswordValid(newPassword))
			throw new PasswordException(PASSWORD_MUST_BE_4_DIGITS);
		if (currentPassword==null)
			throw new PasswordException(CURRENT_PASSWORD_IS_EMPTY);
		synchronized(Paths.PASSWORDS_FILE){
			BufferedReader in = null;
			BufferedWriter bw = null;
			try {
				currentPassword = encodeMD5(currentPassword);
				File namefile = new File(Paths.AIRPI_ROOT+Paths.PASSWORDS_FILE);
				FileReader namereader = new FileReader(namefile);
				in = new BufferedReader(namereader);
				if (currentPassword.equals(in.readLine())) {
					String guest_pwd = in.readLine();
					FileWriter fw = new FileWriter(namefile.getAbsoluteFile());
					bw = new BufferedWriter(fw);
					bw.write(encodeMD5(newPassword)+"\n"+guest_pwd);
				}
				else
					throw new PasswordException(CURRENT_PASSWORD_IS_WRONG);
			}
			catch (NoSuchAlgorithmException e) {
				throw new PasswordException(MD5_ERROR);
			}
			catch (UnsupportedEncodingException e) {
				throw new PasswordException(MD5_ERROR);
			}
			catch (IOException e) {
				throw new PasswordException(ERROR_FILE);
			}
			finally {
				try {
					if (in!=null) in.close();
					if (bw != null) bw.close();
				} catch (IOException ignore) {}
			}
		}
	}
	
	/**
	 * Set the guest password
	 * @param currentPassword The current password
	 * @param newPassword The new password
	 * @throws PasswordException When something went wrong
	 */
	public static void setGuestPassword(String currentPassword, String newPassword) throws PasswordException {
		if (!isThePasswordValid(newPassword))
			throw new PasswordException(PASSWORD_MUST_BE_4_DIGITS);
		if (currentPassword==null)
			throw new PasswordException(CURRENT_PASSWORD_IS_EMPTY);
		synchronized(Paths.PASSWORDS_FILE){
			BufferedReader in = null;
			BufferedWriter bw = null;
			try {
				File namefile = new File(Paths.AIRPI_ROOT+Paths.PASSWORDS_FILE);
				FileReader namereader = new FileReader(namefile);
				in = new BufferedReader(namereader);
				String admin_pwd = in.readLine();
				if (currentPassword.equals(in.readLine())) {
					FileWriter fw = new FileWriter(namefile.getAbsoluteFile());
					bw = new BufferedWriter(fw);
					bw.write(admin_pwd+"\n"+newPassword);
				}
				else
					throw new PasswordException(CURRENT_PASSWORD_IS_WRONG);
			} catch (IOException e) {
				throw new PasswordException(ERROR_FILE);
			}
			finally {
				try {
					if (in != null) in.close();
					if( bw != null) bw.close();
				} catch (IOException ignore) {}
			}
		}
	}
	
	/**
	 * Say wether the given password is the admin's password or the guest's password
	 * @param password
	 * @return True when the given password is the admin's password, or false if it's the guest's password
	 * @throws PasswordException When the given password is NOT a fucking password
	 */
	public static boolean sayTrueIfThisIsTheAdminPasswordOrFalseIfThisIsTheGuestPasswordOrExceptionIfItIsArandomString(String password) throws PasswordException {
		if (password == null) throw new PasswordException(THIS_IS_NOT_A_VALID_PASSWORD);
		synchronized(Paths.PASSWORDS_FILE){
			BufferedReader in = null;
			try {
				File namefile = new File(Paths.AIRPI_ROOT+Paths.PASSWORDS_FILE);
				FileReader namereader = new FileReader(namefile);
				in = new BufferedReader(namereader);
				if (encodeMD5(password).equals(in.readLine())) {
					return true;
				}
				else if (password.equals(in.readLine()))
					return false;
				else
					throw new PasswordException(THIS_IS_NOT_A_VALID_PASSWORD);
			} catch (NoSuchAlgorithmException e) {
				throw new PasswordException(MD5_ERROR);
			}
			catch (UnsupportedEncodingException e) {
				throw new PasswordException(MD5_ERROR);
			}
			catch (IOException e) {
				throw new PasswordException(ERROR_FILE);
			}
			finally {
				try {
					if (in!=null) in.close();
				} catch (IOException ignore) {}
			}
		}
	}
	
	/**
	 * Retrieve the guest's password
	 * @return The guest's password
	 * @throws PasswordException When we were unable to read the fucking file, bitch!
	 */
	public static String getGuestPassword() throws PasswordException {
		//synchronized(Paths.PASSWORDS_FILE){
			BufferedReader in = null;
			try {
				File namefile = new File(Paths.AIRPI_ROOT+Paths.PASSWORDS_FILE);
				if(namefile.exists()){
				FileReader namereader = new FileReader(namefile);
				in = new BufferedReader(namereader);
				in.readLine();
				return in.readLine();
				}else{
					return "";
				}
			}
			catch (IOException e) {
				throw new PasswordException(ERROR_FILE);
			}
			finally {
				try {
					if (in!=null) in.close();
				} catch (IOException ignore) {}
			}
		//}
	}
	
	public long getTimeStamp() {
		return System.currentTimeMillis()-timeStamp;
	}

	public void updateTimeStamp() {
		this.timeStamp = System.currentTimeMillis();
	}
	
	public int getIdSession(){
		return this.idSession;
	}
	@Override
	public int compareTo(User otherUser) {
		if(this.name.equals(otherUser.name))
			return 0;
		else if(this.isAdmin==true && otherUser.isAdmin==false)
			return -1;
		else
			return 1;
	}

	@Override
	public String toString(){
		return name;
	}

	public boolean equals(Object user){
		return (this.name.equals(((User)user).name) && this.isAdmin()==((User)user).isAdmin() && this.getIdSession()==((User)user).getIdSession());
	}
	
	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + (isAdmin ? 1231 : 1237);
		result = prime * result + ((name == null) ? 0 : name.hashCode());
		return result;
	}

}
